﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using TraitorAmongUs.Common;
using TraitorAmongUs.Network;

namespace TraitorAmongUs.States {
	public class PlayingState : Singleton<PlayingState>, GameState {
		Engine engine;

		//Graphics.CharacterSprite testSprite;

		bool isEveryoneLoaded = false;
		ClientPlayer[] players = new ClientPlayer[10];
		Crosshair[] crosshairs = new Crosshair[10];		// crosshair[0] is the local player

		PlayerStates currentState = PlayerStates.IDLE;

		public PlayingState() {
		}

		void GameState.EnterState(Engine e, dynamic param) {
			// Dynamic property example
			//var test = ( param.GetType().GetProperty("test") != null ? param.test : "default value" );

			engine = e;

			// Hide the cursor
			engine.SetCursorVisibility(false);

			for(int i = 0; i < crosshairs.Length; ++i) {
				crosshairs[i] = new Crosshair(engine.Content);
			}

			crosshairs[0].r = 255;
			crosshairs[0].g = 0;
			crosshairs[0].b = 0;

			isEveryoneLoaded = false;

			// Hook the packet events
			engine.networkClient.OnInitialDataPacketReceived += new Client.InitialDataReceivedHandler(networkClient_OnInitialDataPacketReceived);
			engine.networkClient.OnEveryoneLoadedPacketReceived += new Client.EveryoneLoadedReceivedHandler(networkClient_OnEveryoneLoadedPacketReceived);
			engine.networkClient.OnUpdateDataPacketReceived += new Client.UpdateDataReceivedHandler(networkClient_OnUpdateDataPacketReceived);
			engine.networkClient.OnUpdateCrosshairPacketReceived += new Client.UpdateCrosshairReceivedHandler(networkClient_OnUpdateCrosshairPacketReceived);
			engine.networkClient.OnUpdateBulletCountPacketReceived += new Client.UpdateBulletCountReceivedHandler(networkClient_OnUpdateBulletCountPacketReceived);
			engine.networkClient.OnWinnerPacketReceived += new Client.WinnerReceivedHandler(networkClient_OnWinnerPacketReceived);

			engine.inputManager.GetMouse().MouseButtonReleased += new Nuclex.Input.Devices.MouseButtonDelegate(PlayingState_MouseButtonReleased);

			// Send Crosshair Color
			using(Packet packet = new Packet()) {
				packet.Write(crosshairs[0].r);
				packet.Write(crosshairs[0].g);
				packet.Write(crosshairs[0].b);
				packet.Send(engine.networkClient.Stream, Packet.PacketTypes.SERVER_GAME_UPDATE_CROSSHAIR_COLOR);
			}

			// Send loaded packet
			using(Packet packet = new Packet()) {
				packet.Send(engine.networkClient.Stream, Packet.PacketTypes.SERVER_GAME_LOADED);
			}
		}

		void PlayingState_MouseButtonReleased(Nuclex.Input.MouseButtons buttons) {
			if(buttons == Nuclex.Input.MouseButtons.Left) {
				using(Packet packet = new Packet()) {
					int x = engine.inputManager.GetMouse().GetState().X;
					int y = engine.inputManager.GetMouse().GetState().Y;

					packet.Write(x);
					packet.Write(y);

					packet.Send(engine.networkClient.Stream, Packet.PacketTypes.SERVER_GAME_SHOOT);
				}

				// TODO: Play a sound
			}
		}


		void GameState.LeaveState() {
			isEveryoneLoaded = false;

			if(crosshairs != null) {
				for(int i = 0; i < crosshairs.Length; ++i) {
					crosshairs[i] = null;
				}
			}

			if(players != null) {
				for(int i = 0; i < players.Length; ++i) {
					players[i] = null;
				}
			}

			engine.inputManager.GetMouse().MouseButtonReleased -= new Nuclex.Input.Devices.MouseButtonDelegate(PlayingState_MouseButtonReleased);

			// Unhook the packet events
			engine.networkClient.OnInitialDataPacketReceived -= new Client.InitialDataReceivedHandler(networkClient_OnInitialDataPacketReceived);
			engine.networkClient.OnEveryoneLoadedPacketReceived -= new Client.EveryoneLoadedReceivedHandler(networkClient_OnEveryoneLoadedPacketReceived);
			engine.networkClient.OnUpdateDataPacketReceived -= new Client.UpdateDataReceivedHandler(networkClient_OnUpdateDataPacketReceived);
			engine.networkClient.OnUpdateCrosshairPacketReceived -= new Client.UpdateCrosshairReceivedHandler(networkClient_OnUpdateCrosshairPacketReceived);
			engine.networkClient.OnUpdateBulletCountPacketReceived -= new Client.UpdateBulletCountReceivedHandler(networkClient_OnUpdateBulletCountPacketReceived);
			engine.networkClient.OnWinnerPacketReceived -= new Client.WinnerReceivedHandler(networkClient_OnWinnerPacketReceived);
		}

		void GameState.Draw(GameTime gameTime) {
			engine.GraphicsDevice.Clear(Color.Beige);

			if(!isEveryoneLoaded) return;

			engine.spriteBatch.Begin();
			foreach(ClientPlayer player in players) {
				player.Sprite.Draw(gameTime, engine.spriteBatch);
			}

			foreach(Crosshair crosshair in crosshairs) {
				if(crosshair != null) {
					if(crosshair.X <= -(crosshair.sprite.Width / 2) && crosshair.Y <= -(crosshair.sprite.Height / 2)) continue;

					crosshair.Draw(gameTime, engine.spriteBatch);
				}
			}
			engine.spriteBatch.End();
		}

		void GameState.Update(GameTime gameTime) {
			if(!isEveryoneLoaded) return;

			MouseState mouseState = engine.inputManager.GetMouse().GetState();
			crosshairs[0].X = mouseState.X - crosshairs[0].sprite.Width / 2;
			crosshairs[0].Y = mouseState.Y - crosshairs[0].sprite.Height / 2;

			// Send the crosshair position to the server
			using(Packet packet = new Packet()) {
				packet.Write(mouseState.X);
				packet.Write(mouseState.Y);
				packet.Send(engine.networkClient.Stream, Packet.PacketTypes.SERVER_GAME_UPDATE_CROSSHAIR);
			}

			bool currentStateChanged = false;

			// Handle game input
			KeyboardState keyboardState = engine.inputManager.GetKeyboard().GetState();

			var stateKeys = new[] {
				 new { state = PlayerStates.WALKING, key = Keys.Z },
				 new { state = PlayerStates.RUNNING, key = Keys.X },
				 new { state = PlayerStates.IDLE, key = Keys.F24 }
			};

			foreach(var sk in stateKeys) {
				if(keyboardState.IsKeyDown(sk.key) || sk.key == Keys.F24) {
					if(currentState != sk.state) {
						currentState = sk.state;
						currentStateChanged = true;
					}

					break;
				}
			}

			if(currentStateChanged) {
				// Send the new state to the server
				using(Packet packet = new Packet()) {
					packet.Write((int)currentState);
					packet.Send(engine.networkClient.Stream, Packet.PacketTypes.SERVER_GAME_UPDATE_STATE);
				}
			}

			foreach(ClientPlayer player in players) {
				player.Sprite.Update(gameTime);
			}
		}

		// Packets
		void networkClient_OnInitialDataPacketReceived(ServerPlayer[] playerList) {
			for(int i = 0; i < players.Length; ++i) {
				players[i] = new ClientPlayer();
				players[i].Sprite = new Graphics.CharacterSprite(engine.Content, playerList[i].spritePath);
				players[i].X = playerList[i].X;
				players[i].Y = playerList[i].Y;
				players[i].State = playerList[i].State;
			}

			using(Packet packet = new Packet()) {
				packet.Send(engine.networkClient.Stream, Packet.PacketTypes.SERVER_GAME_INIT_DATA_RECEIVED);
			}
		}

		void networkClient_OnUpdateDataPacketReceived(ServerPlayer[] playerList) {
			for(int i = 0; i < players.Length; ++i) {
				players[i].X = playerList[i].X;
				players[i].Y = playerList[i].Y;
				players[i].State = playerList[i].State;
			}
		}

		void networkClient_OnUpdateCrosshairPacketReceived(Crosshair[] crosshairList) {
			for(int i = 0; i < crosshairList.Length; ++i) {
				crosshairs[i + 1].X = crosshairList[i].X - crosshairs[i + 1].sprite.TextureWidth / 2;
				crosshairs[i + 1].Y = crosshairList[i].Y - crosshairs[i + 1].sprite.TextureHeight / 2;
				crosshairs[i + 1].r = crosshairList[i].r;
				crosshairs[i + 1].g = crosshairList[i].g;
				crosshairs[i + 1].b = crosshairList[i].b;
				crosshairs[i + 1].bulletCount = crosshairList[i].bulletCount;
			}

			for(int i = crosshairList.Length + 1; i < crosshairs.Length; ++i) {
				crosshairs[i].X = -255;
				crosshairs[i].Y = -255;
			}
		}

		void networkClient_OnEveryoneLoadedPacketReceived() {
			isEveryoneLoaded = true;
		}

		void networkClient_OnUpdateBulletCountPacketReceived(int bulletCount) {
			crosshairs[0].bulletCount = bulletCount;
		}

		void networkClient_OnWinnerPacketReceived(string username) {
			System.Windows.Forms.MessageBox.Show(string.Format("{0} has won!", username));
			engine.stateManager.SetState(States.LobbyState.Instance, new { Connected = true });
		}
	}
}
